
#include <cstdlib>
#include <iostream>

#include <math.h>

#include "common.h"

#include "SDL.h"
#include "SDL_opengl.h"

#include "BaseShape.h"

using namespace std;

__BEGIN_NAMESPACE_DECL_

// Constructor
BaseShape::BaseShape ()
{
    _offsetX = _offsetY = _offsetZ = 0.0;
    _scaleX  = _scaleY  = _scaleZ  = 1.0;
    _rotX    = _rotY    = _rotZ    = 0.0;
}

// Destructor
BaseShape::~BaseShape ()
{
}

void 
BaseShape::setOffset (double x, double y, double z)
{
    _offsetX = x; _offsetY = y; _offsetZ = z; 
}

void 
BaseShape::setRotation (double rotX, double rotY, double rotZ)
{
    _rotX = rotX; _rotY = rotY; _rotZ = rotZ;
}

void 
BaseShape::setScale (double scaleX, double scaleY, double scaleZ)
{
    _scaleX = scaleX; _scaleY = scaleY; _scaleZ = scaleZ;
}

void 
BaseShape::getOffset (double& x, double& y, double& z) const
{
    x = _offsetX; y = _offsetY; z = _offsetZ;
}


void 
BaseShape::draw3DRectangle (double x1, double y1, 
                            double x2, double y2, double thickness)
{
    glBegin(GL_QUADS);

    // top of 3D rectangle
    glNormal3d ( 0.0, 0.0, 1.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x1,  y1,  0.0);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x2,  y1,  0.0);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x2,  y2,  0.0);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x1,  y2,  0.0);

    // sides of the 3D rectangle
    glNormal3d ( 0.0, -1.0, 0.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x1,  y1,  0.0);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x1,  y1,  -thickness);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x2,  y1,  -thickness);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x2,  y1,  0.0);

    glNormal3d ( 1.0, 0.0, 0.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x2,  y1,  0.0);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x2,  y1,  -thickness);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x2,  y2,  -thickness);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x2,  y2,  0.0);

    glNormal3d ( 0.0, 1.0, 0.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x2,  y2,  0.0);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x2,  y2,  -thickness);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x1,  y2,  -thickness);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x1,  y2,  0.0);

    glNormal3d ( -1.0, 0.0, 0.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x1,  y2,  0.0);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x1,  y2,  -thickness);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x1,  y1,  -thickness);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x1,  y1,  0.0);

    // bottom of 3D rectangle
    glNormal3d ( 0.0, 0.0, -1.0);
    glTexCoord2f (0.0, 0.0); glVertex3d ( x1,  y1,  -thickness);
    glTexCoord2f (0.0, 1.0); glVertex3d ( x2,  y1,  -thickness);
    glTexCoord2f (1.0, 1.0); glVertex3d ( x2,  y2,  -thickness);
    glTexCoord2f (1.0, 0.0); glVertex3d ( x1,  y2,  -thickness);

    glEnd();
}

void 
BaseShape::drawCylinder (double x1, double y1, double radius, 
                         double zOffset, double thickness, int segments)
{
    int i;

    // Draw the bottom of the cylinder at zOffset
    glNormal3d ( 0.0, 0.0, -1.0);
    glBegin (GL_POLYGON);
    for (i=0; i < segments; i++)
    {
        double j = (double)i/(double)segments;
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0), 
                    y1+(radius*sin(j*2.0*M_PI)/2.0), 
                    zOffset);
    }
    glEnd ();

    // Draw the sides of the cylinder, each of which is approximated by a
    // rectangle from the circle that is the top of the cylinder, to the circle
    // that is the bottom of the cylinder.
    glBegin(GL_QUADS);
    for (i=0; i < segments; i++)
    {
        double j = (double)i / (double)segments;

        // Define normal [unit] vector
        glNormal3d ( cos(j*2.0*M_PI), sin(j*2.0*M_PI), 0.0);

        // Vertices 1 & 2
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0), 
                    y1+(radius*sin(j*2.0*M_PI)/2.0), 
                    zOffset);
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0), 
                    y1+(radius*sin(j*2.0*M_PI)/2.0),
                    zOffset+thickness);

        j = (double)(i+1) / (double)segments;

        // Define normal [unit] vector
        glNormal3d ( cos(j*2.0*M_PI), sin(j*2.0*M_PI), 0.0);

        // Vertices 3 & 4
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0), 
                    y1+(radius*sin(j*2.0*M_PI)/2.0), 
                    zOffset+thickness);
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0),
                    y1+(radius*sin(j*2.0*M_PI)/2.0),
                    zOffset);
    }
    glEnd();

    // Draw the top of the cylinder at zOffset+thickness
    glNormal3d ( 0.0, 0.0, 1.0);
    glBegin(GL_POLYGON);
    for (i=0; i < segments; i++)
    {
        double j = (double)i/(double)segments;
        glVertex3d (x1+(radius*cos(j*2.0*M_PI)/2.0), 
                    y1+(radius*sin(j*2.0*M_PI)/2.0), 
                    zOffset+thickness);
    }
    glEnd();
}

void
BaseShape::transform()
{
    glTranslated (_offsetX, _offsetY, _offsetZ);
    glRotated (_rotX, 1.0, 0.0, 0.0);
    glRotated (_rotY, 0.0, 1.0, 0.0);
    glRotated (_rotZ, 0.0, 0.0, 1.0);
    glScaled (_scaleX, _scaleY, _scaleZ);
}

__END_NAMESPACE_DECL_
